package com.forestar.mapControl.utils.mapConvert;

import android.graphics.Matrix;
import android.graphics.PointF;
import android.graphics.Rect;

import com.forestar.mapControl.utils.LogD;
import com.forestar.mapControl.utils.mapConvert.geometry.Envelope;
import com.forestar.mapControl.utils.mapConvert.geometry.GeoPoint;
import com.forestar.mapControl.utils.mapConvert.geometry.coordinatesystem.CoordinateSystem;


//注：MapViewTranslator采用的机制为:地图不动，视口移动，并且地图的中心点和屏幕的中心点一直是关联着的。
public class MapViewTransform
  {
    public Object lockObject = new Object();
    private static final double RADIUS_EARTH_METERS = 6378137;
    private static final float BUFFER_QUERYSIZE = 5;
    private CoordinateSystem coordSystem;
    private GeoPoint outGeoPoint;

    //mapCenterX  117.27902701177098   mapCenterY  30.511346123087435
    //mapCenterX  117.56851123870796   mapCenterY  30.838053343583645

    // 窗口中心对应的画布地理坐标：米单位，随地图移动会变化
    private volatile double mapCenterX = 0F;
    private volatile double mapCenterY = 0F;

    // 窗口中心象素坐标，窗口大小不变，此值固定不变
    private double viewWidthInPixels = 400.0F;
    private double viewHeightInPixels = 400.0F;
    private double screenCenterX = 200.0F;
    private double screenCenterY = 200.0F;

    private double resolutionOnMapUnit = 1.0;

    // 屏幕象素密度
    private float sceenViewDPI = 300;

    private long SCALE_LIMIT = 200000000;
    private Rect screenRect = new Rect();
    private Rect screeenSkewRect = new Rect();

    //本次偏移的距离,,,影像rect的起始位置
    private volatile double distanceX = 0;
    private volatile double distanceY = 0;

    public double getDistanceX() {
      return distanceX;
    }

    public double getDistanceY() {
      return distanceY;
    }

    @Override
    public String toString()
      {
        return "X:" + mapCenterX + ",Y:" + mapCenterY + "," + "分辨率：" + resolutionOnMapUnit;
      }

    public MapViewTransform()
      {
      }

    public MapViewTransform(CoordinateSystem coordSystem, double screenWidthSize, double screenHeightSize, double mapScale, PointF centerPoint, int screenDPI)
      {
        viewWidthInPixels = screenWidthSize;
        viewHeightInPixels = screenHeightSize;
        screenCenterX = viewWidthInPixels / 2 + centerPoint.x;
        screenCenterY = viewHeightInPixels / 2 + centerPoint.y;
        this.coordSystem = coordSystem;
        outGeoPoint = new GeoPoint(coordSystem, 0, 0);
        this.setMapScale(mapScale);
        sceenViewDPI = screenDPI;

      }

    @Override
    public MapViewTransform clone()
      {
            MapViewTransform mvTrans = new MapViewTransform();
            mvTrans.coordSystem = coordSystem;
            mvTrans.outGeoPoint = outGeoPoint.clone();
            // 窗口中心对应的画布地理坐标：米单位，随地图移动会变化
            mvTrans.mapCenterX = mapCenterX;
            mvTrans.mapCenterY = mapCenterY;
            // 窗口中心象素坐标，窗口大小不变，此值固定不变
            mvTrans.viewWidthInPixels = viewWidthInPixels;
            mvTrans.viewHeightInPixels = viewHeightInPixels;
            mvTrans.screenCenterX = screenCenterX;
            mvTrans.screenCenterY = screenCenterY;
            mvTrans.resolutionOnMapUnit = resolutionOnMapUnit;
            // 屏幕象素密度
            mvTrans.sceenViewDPI = sceenViewDPI;
            return mvTrans;
      }

    public void clone(MapViewTransform transform)
      {
        transform.coordSystem = coordSystem;
        transform.outGeoPoint = outGeoPoint.clone();
        // 窗口中心对应的画布地理坐标：米单位，随地图移动会变化
        transform.mapCenterX = mapCenterX;
        transform.mapCenterY = mapCenterY;
        // 窗口中心象素坐标，窗口大小不变，此值固定不变
        transform.viewWidthInPixels = viewWidthInPixels;
        transform.viewHeightInPixels = viewHeightInPixels;
        transform.screenCenterX = screenCenterX;
        transform.screenCenterY = screenCenterY;
        transform.resolutionOnMapUnit = resolutionOnMapUnit;
        // 屏幕象素密度
        transform.sceenViewDPI = sceenViewDPI;
      }

    public void clone2(MapViewTransform transform)
      {
        transform.coordSystem = coordSystem;
        transform.outGeoPoint = outGeoPoint.clone();
        // 窗口中心象素坐标，窗口大小不变，此值固定不变
        transform.viewWidthInPixels = viewWidthInPixels;
        transform.viewHeightInPixels = viewHeightInPixels;
        transform.screenCenterX = screenCenterX;
        transform.screenCenterY = screenCenterY;
        transform.resolutionOnMapUnit = resolutionOnMapUnit;
        // 屏幕象素密度
        transform.sceenViewDPI = sceenViewDPI;
      }

    public CoordinateSystem getCoordinateSystem()
      {
        return coordSystem;
      }

    public void setCoordinateSystem(CoordinateSystem coordSystem)
      {
        this.coordSystem = coordSystem;
        outGeoPoint = new GeoPoint(coordSystem, 0, 0);
      }

    // 设置视口的大小
    public final void setViewSize(double screenWidthSize, double screenHeightSize)
      {
        viewWidthInPixels = screenWidthSize;
        viewHeightInPixels = screenHeightSize;
        screenCenterX = viewWidthInPixels / 2;
        screenCenterY = viewHeightInPixels / 2;
      }

    // 获取地图中心的X值
    public final double getMapCenterX()
      {
        return mapCenterX;
      }

    // 获取地图中心的Y值
    public final double getMapCenterY()
      {
        return mapCenterY;
      }

    /**
     * 定位到指定位置，使用地图坐标
     *
     * @param mapX
     * @param mapY
     */

    public final void setMapCenter(double mapX, double mapY)
      {
        mapCenterX = mapX;
        mapCenterY = mapY;
      }

    /**
     * 地图全图
     */
    public final void fullExtent(double mapScale, double centerX, double centerY)
      {
        mapCenterX = centerX;
        mapCenterY = centerY;
        this.setMapScale(mapScale);
      }

    public final void zoomIn()
      {
        resolutionOnMapUnit /= 2.0;
        scaleLimit();
      }

    public final void zoomInAt(PointF atPoint)
      {
        synchronized (lockObject)
          {
            GeoPoint zoomPt = screenPoint2MapPoint(atPoint);
            setMapCenter(zoomPt);
            resolutionOnMapUnit /= 2.0;
            PointF centerPt = getScreenCenter();
            float dx = atPoint.x - centerPt.x;
            float dy = atPoint.y - centerPt.y;
            move(dx, dy);
          }
      }

    public final void zoomOut()
      {
        resolutionOnMapUnit *= 2.0F;
        scaleLimit();
      }

    public final void zoom(double zoomScale)
      {
        setMapScale(zoomScale);
        scaleLimit();
      }

    public final void MoveHorizontal(float movePixels)
      {
        mapCenterX -= movePixels * resolutionOnMapUnit;
      }

    public final void MoveVertical(float movePixels)
      {
        mapCenterY += movePixels * resolutionOnMapUnit;
      }

    public final float[] mapPoints2ScreenPoints(double[] mapPoints)
      {
        float[] screenPoints = new float[mapPoints.length];
        for (int i = 0; i < screenPoints.length / 2; i++)
          {
            double screenX = screenCenterX + (long) ((mapPoints[2 * i] - mapCenterX) / resolutionOnMapUnit + 0.5);
            double screenY = screenCenterY - (long) ((mapPoints[2 * i + 1] - mapCenterY) / resolutionOnMapUnit + 0.5);
            screenPoints[2 * i] = (float) screenX;
            screenPoints[2 * i + 1] = (float) screenY;
          }
        return screenPoints;
      }

    // 之所以X用加（+），Y用减（-），是因为屏幕坐标的坐标方向为：x向右为正，y向下为正；
    // 地理坐标的坐标方向为：x向右为正，y向上为正；X的正方向两者相同，Y的正方向两者相反
    public final PointF mapPoint2ScreenPoint(GeoPoint point)
      {
        if (!point.getCoordinateSystem().equals(this.coordSystem))
          {
            CoordinateSystem.projectPoint(point, outGeoPoint);
          } else
          {
            outGeoPoint.setX(point.getX());
            outGeoPoint.setY(point.getY());
          }
        double screenX = screenCenterX + (long) ((outGeoPoint.getX() - mapCenterX) / resolutionOnMapUnit - 0.5);
        double screenY = screenCenterY - (long) ((outGeoPoint.getY() - mapCenterY) / resolutionOnMapUnit + 0.5);
        float x = (float) screenX;
        float y = (float) screenY;
        return new PointF(x, y);
      }

    public final GeoPoint screenPoint2MapPoint(PointF point)
      {
        double realX = mapCenterX + ((point.x - screenCenterX)) * resolutionOnMapUnit;
        double realY = mapCenterY - ((point.y - screenCenterY)) * resolutionOnMapUnit;
        return new GeoPoint(coordSystem, realX, realY);
      }

    public final GeoPoint screenPoint2MapPoint(float pointX, float pointY)
      {
        double realX = mapCenterX + ((pointX - screenCenterX)) * resolutionOnMapUnit;
        double realY = mapCenterY - ((pointY - screenCenterY)) * resolutionOnMapUnit;
        return new GeoPoint(coordSystem, realX, realY);
      }

//    public QueryFilter createQueryFilter()
//      {
//        Envelope envelope = createShowEnvelope();
//        QueryFilter qf = new QueryFilter("", "", envelope, this.getResolution());
//        return qf;
//      }

    public Envelope createShowEnvelope()
      {
        PointF leftUpper = new PointF(-BUFFER_QUERYSIZE, -BUFFER_QUERYSIZE);
        PointF rightLower = new PointF((float) viewWidthInPixels + BUFFER_QUERYSIZE, (float) viewHeightInPixels + BUFFER_QUERYSIZE);
        GeoPoint wrdLeftUpper = this.screenPoint2MapPoint(leftUpper);
        GeoPoint wrdRightLower = this.screenPoint2MapPoint(rightLower);
        double xMin = Math.max(wrdLeftUpper.getX(), coordSystem.getXMin());
        double xMax = Math.min(wrdRightLower.getX(), coordSystem.getXMax());
        double yMin = Math.max(wrdRightLower.getY(), coordSystem.getYMin());
        double yMax = Math.min(wrdLeftUpper.getY(), coordSystem.getYMax());
        Envelope envelope = new Envelope(coordSystem, xMin, xMax, yMin, yMax);
        return envelope;
      }

    // 获取视口左下角的地理坐标
    private final GeoPoint getViewLBMapPoint()
      {
        double x = (float) (mapCenterX - (viewWidthInPixels / 2) * resolutionOnMapUnit);
        double y = (float) (mapCenterY - (viewHeightInPixels / 2) * resolutionOnMapUnit);
        return new GeoPoint(coordSystem, x, y);
      }

    // 获取视口右上角的地理坐标
    private final GeoPoint getViewRTMapPoint()
      {
        double x = (float) (mapCenterX + (viewWidthInPixels / 2) * resolutionOnMapUnit);
        double y = (float) (mapCenterY + (viewHeightInPixels / 2) * resolutionOnMapUnit);
        return new GeoPoint(coordSystem, x, y);
      }

    public Envelope getViewBound()
      {
        GeoPoint viewLBPoint = getViewLBMapPoint();
        GeoPoint viewRTPoint = getViewRTMapPoint();
        Envelope viewBound = new Envelope(coordSystem, viewLBPoint.getX(), viewRTPoint.getX(), Math.min(viewLBPoint.getY(), viewRTPoint.getY()), Math.max
            (viewLBPoint.getY(), viewRTPoint.getY()));
        return viewBound;
      }

    public Rect getViewRect()
      {
        int left = (int) (screenCenterX - viewWidthInPixels / 2);
        int top = (int) (screenCenterY - viewHeightInPixels / 2);
        return new Rect(left, top, left + (int) viewWidthInPixels, top + (int) viewHeightInPixels);
      }

    public void move(float moveXPixels, float moveYPixels)
      {
        LogD.d("前:偏移前的中心点："+mapCenterX+"<---->"+mapCenterY);
        double centenrXQ=mapCenterX;
        double centenrYQ=mapCenterY;

        mapCenterX -= moveXPixels * resolutionOnMapUnit;
        mapCenterY += moveYPixels * resolutionOnMapUnit;
        LogD.d("后:偏移后的中心点："+mapCenterX+"<---->"+mapCenterY);

        distanceX = -(mapCenterX-centenrXQ);
        distanceY =  +(mapCenterY-centenrYQ);
        LogD.d("后:偏移后的距离点："+distanceX+"<---->"+distanceY);
      }

    // 以米为单位的分辨率，可以直接用来计算比例尺的
    public double getResolution()
      {
        if (this.coordSystem == null)
          {
            return 0;
          }
        double meterPerPixel = 0;
        if (this.coordSystem.isGeographicCoordinateSystem())
          {
            double[] xy = new double[2];
            CoordinateSystem.Mercator2lonLat(mapCenterX, mapCenterY, xy);
            // 求算周长
            double lat = xy[1];
            double circumference = Math.cos(lat * Math.PI / 180) * 2 * Math.PI * RADIUS_EARTH_METERS;
            // 单象素长度（米）= 单象素占度数 * 周长
            meterPerPixel = resolutionOnMapUnit * (circumference / 360);

          } else if (this.coordSystem.isWebMercator())
          {
            double[] xy = new double[2];
            CoordinateSystem.Mercator2lonLat(mapCenterX, mapCenterY, xy);
            // 求算周长
            double lat = xy[1];
            meterPerPixel = Math.cos(lat * Math.PI / 180) * resolutionOnMapUnit;
          } else
          {
            meterPerPixel = resolutionOnMapUnit;
          }
        return meterPerPixel;
      }

    public double getMapScale()
      {
        // 屏幕1米代表的实际地理长度
        double pixelsPerMeterOnScreen = sceenViewDPI / 0.0254;
        return getResolution() * pixelsPerMeterOnScreen;
      }

    // 设置比例尺  mScale 有几个操作等级？
    public final void setMapScale(double mScale)
      {
        if (this.coordSystem == null)
          {
            return;
          }
        double pixelsPerMeterOnScreen = sceenViewDPI / 0.0254;
        double meterPerPixel = mScale / pixelsPerMeterOnScreen;
        if (this.coordSystem.isGeographicCoordinateSystem())
          {
            double[] xy = new double[2];
            CoordinateSystem.Mercator2lonLat(mapCenterX, mapCenterY, xy);
            // 求算周长
            double lat = xy[1];
            double circumference = Math.cos(lat * Math.PI / 180) * 2 * Math.PI * RADIUS_EARTH_METERS;
            resolutionOnMapUnit = meterPerPixel / (circumference / 360);
          } else if (this.coordSystem.isWebMercator())
          {
            double[] xy = new double[2];
            CoordinateSystem.Mercator2lonLat(mapCenterX, mapCenterY, xy);
            // 求算周长
            double lat = xy[1];
            resolutionOnMapUnit = meterPerPixel / Math.cos(lat * Math.PI / 180);
          } else
          {
            resolutionOnMapUnit = meterPerPixel;
          }
        scaleLimit();
      }
    // 地图比例
    // private double mapScale = 100000000; // 1000.0F;
    // 地球平均半径为6371Km,
    // 地球上任一纬度圈的半径=地球平均半径*cos纬度值
    // 然后套用圆周长公式计算

    public double getResolutionInMapUnit()
      {
        return resolutionOnMapUnit;
      }

    public void setDensityDPI(float density)
      {
        sceenViewDPI = density;
      }

    public double getScreenCenterX()
      {
        return screenCenterX;
      }

    public double getScreenCenterY()
      {
        return screenCenterY;
      }

    public PointF getScreenCenter()
      {
        return new PointF((float) screenCenterX, (float) screenCenterY);
      }

    public void setMapCenter(GeoPoint centerPoint)
      {
        CoordinateSystem.projectPoint(centerPoint, outGeoPoint);
        this.setMapCenter(outGeoPoint.getX(), outGeoPoint.getY());
      }

    public Matrix matrixToOther(MapViewTransform transform)
      {
        double scale = this.resolutionOnMapUnit / transform.resolutionOnMapUnit;
        double offsetX = (this.mapCenterX - transform.mapCenterX) / transform.resolutionOnMapUnit;
        double offsetY = (transform.mapCenterY - this.mapCenterY) / transform.resolutionOnMapUnit;
        Matrix mt = new Matrix();
        mt.setScale((float) scale, (float) scale);
        mt.postTranslate((float) offsetX, (float) offsetY);
        return mt;
      }

    public GeoPoint getCenterPoint()
      {
        return new GeoPoint(this.coordSystem, this.mapCenterX, this.mapCenterY);
      }

    public void zoomWithFactor(double zoomFactor)
      {
        resolutionOnMapUnit *= zoomFactor;
        scaleLimit();
      }

    /**
     * 比例缩放比例做限制，防止无限缩小的异常
     */
    public void scaleLimit()
      {
        if (SCALE_LIMIT < getMapScale())
          {
            setMapScale(SCALE_LIMIT - 1);
          }

      }

    /**
     * 获取屏幕的矩形范围
     *
     * @return 屏幕的矩形范围
     */
    public Rect getScreenRect()
      {
        screenRect.left = 0;
        screenRect.top = 0;
        screenRect.right = (int) (screenCenterX * 2);
        screenRect.bottom = (int) (screenCenterY * 2);
        return screenRect;
      }

    /**
     * 获取基于transormSrc的偏移屏幕范围
     *
     * @param transformSrc 偏移基础
     * @return 屏幕范围
     */
    public Rect getScreenRect(MapViewTransform transformSrc)
      {
        double resolutionScale = (transformSrc.getResolutionInMapUnit() / resolutionOnMapUnit);
        int width = (int) (transformSrc.getScreenCenterX() * 2 * resolutionScale);
        int height = (int) (transformSrc.getScreenCenterY() * 2 * resolutionScale);
        PointF originPoint = mapPoint2ScreenPoint(transformSrc.screenPoint2MapPoint(0, 0));
        screeenSkewRect.left = (int) originPoint.x;//放大地图view的宽度，解决计算误差导致的边界白边的问题，放大10个像素，10是经验值
        // (之前是3，在平板上测试发现有偏7个像素的情况，现在改为10);
        screeenSkewRect.top = (int) originPoint.y;
        screeenSkewRect.right = (int) originPoint.x + width;// 放大地图view的宽度，解决计算误差导致的边界白边的问题，放大10个像素，10是经验值;
        screeenSkewRect.bottom = (int) originPoint.y + height;
        return screeenSkewRect;
      }
  }
